Skip to content

feat: block selection policy #242

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 22 commits into from
May 26, 2025
Merged

feat: block selection policy #242

merged 22 commits into from
May 26, 2025

Conversation

0xKitsune
Copy link
Collaborator

@0xKitsune 0xKitsune commented May 21, 2025

This PR addresses #133 and contains a slightly alternative approach to #134

This PR introduces support for a block selection policy that determines whether to return the builder block or the default L2 execution client block when both are available.

    async fn get_payload(
        &self,
        payload_id: PayloadId,
        version: PayloadVersion,
    ) -> RpcResult<OpExecutionPayloadEnvelope> {
           // --snip--
           
            if let Ok(Some(builder_payload)) = builder_payload {
                if self.execution_mode().is_dry_run() {
                    (l2_payload, PayloadSource::L2)
                } else if let Some(selection_policy) = &self.block_selection_policy {
                    selection_policy.select_block(builder_payload, l2_payload)
                }

                //--snip--
     };

The initial policy implemented compares the gas used in the builder and L2 blocks. If the builder block uses less than 10% of the gas used by the L2 block, the L2 block is selected instead. This ensures that empty or severely underfilled builder blocks are not propagated to the network.

/// Defines the strategy for choosing between the builder block and the L2 client block
/// during block production.
#[derive(Serialize, Deserialize, Debug, Copy, Clone, PartialEq, clap::ValueEnum)]
pub enum BlockSelectionPolicy {
    /// Selects the block based on gas usage.
    ///
    /// If the builder block uses less than 10% of the gas used by the L2 client block,
    /// the L2 block is selected instead. This prevents propagation of valid but empty
    /// builder blocks and mitigates issues where the builder is not receiving enough
    /// transactions due to networking or peering failures.
    GasUsed,
}

impl BlockSelectionPolicy {
    pub fn select_block(
        &self,
        builder_payload: OpExecutionPayloadEnvelope,
        l2_payload: OpExecutionPayloadEnvelope,
    ) -> (OpExecutionPayloadEnvelope, PayloadSource) {
        match self {
            BlockSelectionPolicy::GasUsed => {
                let builder_gas = builder_payload.gas_used() as f64;
                let l2_gas = l2_payload.gas_used() as f64;

                // Select the L2 block if the builder block uses less than 90% of the gas.
                // This avoids selecting empty or severely underfilled blocks,
                if builder_gas < l2_gas * 0.1 {
                    (l2_payload, PayloadSource::L2)
                } else {
                    (builder_payload, PayloadSource::Builder)
                }
            }
        }
    }
}

While this threshold could be made configurable, it is fixed at 10% for now to avoid edge cases. For example, it is possible that a user submits a high fee transaction that consumes the entire block gas limit and the builder does not include the transaction (e.g. due to a custom ordering policy). If the threshold is set higher (e.g. 50%) and the builder's local mempool only has enough transactions to meet the gas target, the resulting block may appear ~50% less utilized than the default client’s block. This could enable malicious actors to delay or censor transactions from builder blocks.

To mitigate this, the gas used threshold is fixed at a conservative 10% which ensures that empty or severely underfilled builder blocks are not propagated to the network without introducing potential attack vectors to censor user txs.

This PR also moves Payload related types into a new a new module to reduce moving parts in server.rs.

Copy link

vercel bot commented May 21, 2025

The latest updates on your projects. Learn more about Vercel for Git ↗︎

1 Skipped Deployment
Name Status Preview Comments Updated (UTC)
rollup-boost ⬜️ Ignored (Inspect) Visit Preview May 23, 2025 2:51am

@ferranbt ferranbt requested a review from avalonche May 22, 2025 14:21
@ferranbt ferranbt self-requested a review May 26, 2025 10:46
@ferranbt ferranbt merged commit 443d3db into main May 26, 2025
6 of 7 checks passed
@0xKitsune 0xKitsune deleted the feat/block-selection-policy branch May 27, 2025 19:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants